package net.yienyien.process.bpel.generator.controlFlow;

import net.yienyien.process.bpd.BPDModel;
import net.yienyien.process.bpd.BPDObject;
import net.yienyien.process.bpd.ControlFlowRelation;
import net.yienyien.process.bpd.IntermediateEvent;
import net.yienyien.process.bpd.Task;
import net.yienyien.process.bpel.generator.Production;
import net.yienyien.process.bpel.generator.impl.ProductionImpl;
import net.yienyien.process.folder.BPDFoldListener;
import net.yienyien.process.folder.artifacts.PatternedBPDComponent;
import net.yienyien.process.folder.stdPatterns.util.StdPatternsSwitch;

import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xml.type.XMLTypePackage;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.AbstractFactory;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TActivity;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TBooleanExpr;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TDurationExpr;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TFlow;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TOnAlarmPick;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TOnMessage;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TPick;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TSequence;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.TWhile;
import org.open.oasis.docs.wsbpel._2._0.process.abstract_.util.ActivitySetter;

import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public class ControlFlowContribListener extends StdPatternsSwitch<TActivity> implements BPDFoldListener {

	private static final String NAME = "net.yienyien.process.bpel.generator.DataFlowContrib";
	private ProductionImpl production;
	private PatternedBPDComponent component;
	private TActivity last;
	private ControlExpressionHandler cexp;
	
	protected ControlFlowContribListener(Production production) {
		this.production=(ProductionImpl) production;
		cexp=new ControlExpressionHandler(production);
	}
	
	@Override
	public String getName() {
		return NAME;
	}

	private TActivity resolveActivity(BPDObject o) {
		TActivity activity=(TActivity) production.getTaskMappings().get(o);
		
		if(activity==null && o.getFrom()!=null) {
			String stg=getFromRepresentation(o.getFrom());
			if(cexp.isControlExpression(stg)) {
				activity=cexp.createControlExpression(stg);
			}
		}
		
		assert(activity!=null): "Unknow "+o;
		return activity;
	}
	
	private TOnMessage resolveOnMessage(BPDObject o) {
		TOnMessage onMessage=(TOnMessage) production.getTaskMappings().get(o);
		
		return onMessage;
	}
	
	
	@Override
	public void fold(BPDModel model, PatternedBPDComponent pcomp) {
		assert(model!=null);
		assert(pcomp!=null);
		this.component=pcomp;
		last=(TActivity) doSwitch(pcomp.getPattern());
		production.getTaskMappings().put(pcomp, last);
	}
	
	@Override
	public TActivity caseFlow(net.yienyien.process.folder.stdPatterns.Flow object) {
		TFlow flow=AbstractFactory.eINSTANCE.createTFlow();
		for(BPDObject o:component.getObjects()) {
			if(!component.getSource().equals(o) && !component.getSink().equals(o)) {
				TActivity activity=resolveActivity(o);
				ActivitySetter.add(flow.getActivity(), activity);
			}
		}
		return flow;
	}

	@Override
	public TActivity caseSequence(net.yienyien.process.folder.stdPatterns.Sequence object) {
		TSequence sequence=AbstractFactory.eINSTANCE.createTSequence();
		BPDObject current=component.getSource();
		
		while(!current.equals(component.getSink())) {
			TActivity activity=resolveActivity(current);
			ActivitySetter.add(sequence.getActivity(), activity);
			current=current.getOutputs().get(0).getOutput();
		}
		TActivity activity=resolveActivity(component.getSink());
		ActivitySetter.add(sequence.getActivity(), activity);

		return sequence;
	}

	@Override
	public TActivity caseRepeatWhile(net.yienyien.process.folder.stdPatterns.RepeatWhile object) {
		TActivity a1;
		TActivity a2;
		TSequence top;
		TWhile loop;
		TSequence iSeq;
		TBooleanExpr cond;
		
		{
			Task t1 = (Task) component.getSource().getOutputs().get(0)
					.getOutput();
			Task t2 = null;
			for (BPDObject o : component.getObjects()) {
				if (!component.getSource().equals(o)
						&& !component.getSink().equals(o) && !t1.equals(o)) {
					t2 = (Task) o;
					break;
				}
			}
			a1 = resolveActivity(t1);
			a2 = resolveActivity(t2);
		}
		
		top=AbstractFactory.eINSTANCE.createTSequence();
		loop=AbstractFactory.eINSTANCE.createTWhile();
		iSeq=AbstractFactory.eINSTANCE.createTSequence();
		TActivity a1bis=(TActivity) EcoreUtil.copy(a1);
		
		
		ActivitySetter.add(top.getActivity(), a1bis);
		ActivitySetter.add(top.getActivity(), loop);
		
		// FIXME
		cond=AbstractFactory.eINSTANCE.createTBooleanExpr();
		cond.getAny().add(XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Text(), getFromRepresentation(component.getSink().getFrom()));
		loop.setCondition(cond);
		
		
		
		ActivitySetter.set(loop, iSeq);
		ActivitySetter.add(iSeq.getActivity(), a1);
		ActivitySetter.add(iSeq.getActivity(), a2);
		
		return top;
	}
	
	@Override
	public TActivity casePick(net.yienyien.process.folder.stdPatterns.Pick object) {
		TPick pick=AbstractFactory.eINSTANCE.createTPick();
		for(ControlFlowRelation out:component.getSource().getOutputs()) {
			IntermediateEvent e=(IntermediateEvent) out.getOutput();
			TOnMessage guard=resolveOnMessage(e);
			
			Task t=(Task) ((out.getOutput().getOutputs().size()>0)? out.getOutput().getOutputs().get(0).getOutput(): null);
			TActivity a=resolveActivity(t);
							
			if(guard!=null && a!=null) {
				ActivitySetter.set(guard, a);
				pick.getOnMessage().add(guard);
			} else {
				TOnAlarmPick onAlarm=AbstractFactory.eINSTANCE.createTOnAlarmPick();
				TDurationExpr expr=AbstractFactory.eINSTANCE.createTDurationExpr();
				// FIXME
				expr.getAny().add(XMLTypePackage.eINSTANCE.getXMLTypeDocumentRoot_Text(), getFromRepresentation(e.getFrom()));
				onAlarm.setFor(expr);
				
				pick.getOnAlarm().add(onAlarm);
				
				ActivitySetter.set(onAlarm, a);
				
				
			}
		}
		return pick;
	}

	private String getFromRepresentation(EObject o) {
		for(EAttribute a:o.eClass().getEAllAttributes()) {
			if("name".equals(a.getName())) {
				return o.eGet(a).toString();
			} else if("Name".equals(a.getName())) {
				return o.eGet(a).toString();
			}
		}
		return "";
	}
	
	
	@Override
	public TActivity caseRepeat(net.yienyien.process.folder.stdPatterns.Repeat object) {
		AbstractFactory.eINSTANCE.createTWhile();
		throw new NotImplementedException();
	}

	@Override
	public TActivity caseSwitch(net.yienyien.process.folder.stdPatterns.Switch object) {
		AbstractFactory.eINSTANCE.createTIf();
		throw new NotImplementedException();
	}

	@Override
	public TActivity caseWhile(net.yienyien.process.folder.stdPatterns.While object) {
		AbstractFactory.eINSTANCE.createTWhile();
		throw new NotImplementedException();
	}
	
	@Override
	public TActivity caseSynchronizingProcess(net.yienyien.process.folder.stdPatterns.SynchronizingProcess object) {
		throw new NotImplementedException();
	}

	@Override
	public TActivity caseOther(net.yienyien.process.folder.stdPatterns.Other object) {
		throw new NotImplementedException();
	}

	@Override
	public TActivity defaultCase(EObject object) {
		assert(false);
		return null;
	}

	public void pre(BPDModel model) {
		
	}
	
	public void post(BPDModel model) {
		if(model.isTrivial()) {
			TSequence top=AbstractFactory.eINSTANCE.createTSequence();
			
			TActivity s=resolveActivity(component.getEntry());
			TActivity e=resolveActivity(component.getExit());
			
			ActivitySetter.add(top.getActivity(), s);
			ActivitySetter.add(top.getActivity(), last);
			ActivitySetter.add(top.getActivity(), e);
			
			ActivitySetter.set(production.getProcess(), top);
		}
	}
	
}
